/**********************************************************************************
**
** Copyright (C) 2009 Jostein Bratlie
** Contact: jostein.bratlie@gmail.com | http://bratlie.info | http://episteme.hin.no
**
** This file is part of GMQtExt, a GMlib and Qt extension library
**
** GMQtExt is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** GMQtExt is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with GMQtExt.  If not, see <http://www.gnu.org/licenses/>.
**
**********************************************************************************/


// Qt
#include <QtCore>

// Qt Solutions
#include <qttreepropertybrowser.h>

// Local
#include "editorfactory.h"
#include "propertymanager.h"



// header
#include "sceneobjectpropertybrowser.h"


// GMlib
#include <gmSceneObject>
#include <gmParametrics>


namespace GMQtExt {


  SceneObjectPropertyBrowser::SceneObjectPropertyBrowser( QWidget* parent ) : PropertyBrowser( parent ) {


    _sceneobject = 0x0;

    _plot_manager = new SurfacePlotPropertyManager;
    getBrowser()->setFactoryForManager( _plot_manager, new SurfacePlotEditorFactory );
  }

  void SceneObjectPropertyBrowser::_setupDisplayObjectProperties( GMlib::DisplayObject* /*obj*/ ) {

    //////////////////////
    // DisplayObject Group
    QtProperty *displayobject;
    displayobject = createGroup( "DisplayObject", "displayobject", getBrowser(), false );

  }

  void SceneObjectPropertyBrowser::_setupPCircleFloatProperties( GMlib::PCircle<float> *obj ) {

    ////////////////
    // PCircle Group
    QtProperty *pcircle;
    pcircle = createGroup( "PCircle<float>", "pcircle", getBrowser(), true );
    setBackgroundColor( pcircle, QColor( "pink" ) );

    // Radius
    createVariantProperty( QVariant::Double, "Radius", obj->getRadius(), "pcircle_float_radius", pcircle );
  }

  void SceneObjectPropertyBrowser::_setupPCurveFloatProperties( GMlib::PCurve<float>* obj ) {


    //////////////
    // PCurve Group
    QtProperty *pcurve;
    pcurve = createGroup( "PCurve<float>", "pcurve", getBrowser(), true );
    setBackgroundColor( pcurve, QColor("LightGreen") );

    ///////
    // Info
    QtProperty *info;
    info = createGroup( "Info", "pcurve_info", pcurve, false );

    // Domain
    createVariantLabel(
        QVariant::String, "Domain",
        QString("[%1, %2]")
        .arg(obj->getParStart())
        .arg(obj->getParEnd()),
        "so_pcurve_info_domain_u",
        info
        );

    // Closed
    createVariantLabel(
        QVariant::String, "Closed",
        QString("%1")
        .arg( obj->isClosed() ? "closed" : "open" ),
        "so_pcurve_info_closed",
        info );
  }

  void SceneObjectPropertyBrowser::_setupPERBSSurfFloatProperties( GMlib::PERBSSurf<float>* /*obj*/ ) {

  //  QtProperty *erbs;
  //  erbs = _group_manager->addProperty( "PERBSSurf<float>" );
  //
  //  // PERBSSurf Properties
  ////  QtProperty *localpatches;
  ////  localpatches = _group_manager->addProperty( "Local Patches" );
  ////  erbs->addSubProperty( localpatches );
  ////
  ////  QtProperty *lp_visible;
  ////  lp_visible = _bool_manager->addProperty( "Visible" );
  ////  _bool_manager->setValue( lp_visible, _perbssurf_float->isLocalPatchesVisible() );
  ////
  ////  localpatches->addSubProperty( lp_visible );
  //
  //
  //  // Insert property in browser
  //  _pb->addProperty( erbs );
  }

  void SceneObjectPropertyBrowser::_setupPSurfFloatProperties( GMlib::PSurf<float>* obj ) {


    //////////////
    // PSurf Group
    QtProperty *psurf;
    psurf = createGroup( "PSurf<float>", "psurf", getBrowser(), true );
    setBackgroundColor( psurf, QColor( "LightGreen" ) );

    ///////
    // Info
    QtProperty *info;
    info = createGroup( "Info", "psurf_info", psurf );

    // Domain U
    createVariantLabel(
        QVariant::String, "Domain U",
        QString("[%1, %2]")
        .arg( obj->getParStartU() )
        .arg( obj->getParEndU() ),
        "so_psurf_info_domain_u",
        info
        );

    // Domain V
    createVariantLabel(
        QVariant::String, "Domain V",
        QString("[%1, %2]")
        .arg( obj->getParStartV() )
        .arg( obj->getParEndV() ),
        "so_psurf_info_domain_v",
        info
        );

    // Closed
    createVariantLabel(
        QVariant::String, "Closed(u/v)",
        QString("%1/%2")
        .arg( obj->isClosedU() ? "closed" : "open" )
        .arg( obj->isClosedV() ? "closed" : "open" ),
        "so_psurf_info_closed",
        info
        );

    ///////
    // Plot
    QtProperty *plot;
    plot = _plot_manager->addProperty( "Plot" );
    _plot_manager->setNoSamples( plot, GMlib::Point2D<int>( obj->getSamplesU(), obj->getSamplesV() ) );
    _plot_manager->setNoDerivatives( plot, GMlib::Point2D<int>( obj->getDerivativesU(), obj->getDerivativesV() ) );
    psurf->addSubProperty( plot );
    registerProperty( plot, "psurf_plot", true );
  }

  void SceneObjectPropertyBrowser::_setupSceneObjectProperties( GMlib::SceneObject* obj ) {

    ////////////////////
    // SceneObject Group
    QtProperty *sceneobject;
    sceneobject = createGroup( tr("SceneObject"), "sceneobject", getBrowser(), true );
    setBackgroundColor( sceneobject, QColor("LightYellow") );


    ///////
    // Info
    QtProperty *info;
    info = createGroup( tr("Info"), "sceneobject_info", sceneobject, true );

    // Identity
    createVariantLabel( QVariant::String, tr("Identity"), obj->getIdentity().c_str(), "so_info_identity", info );

    // Name
    createVariantLabel( QVariant::String, tr("Name"), QString("%1").arg(obj->getName()), "so_info_name", info );

    // Type ID
    createVariantLabel( QVariant::String, tr("TypeID"), QString("0x%1").arg(obj->getTypeId(), 0, 16 ), "so_info_typeid", info );

    // Parent
    if( obj->getParent() ) {

      GMlib::SceneObject *p = obj->getParent();
      createVariantLabel(
          QVariant::String,
          "Parent",
          QString("%1 %2 [%3]")
          .arg(p->getName())
          .arg(p->getIdentity().c_str())
          .arg(p->getTypeId(), 0, 16),
          "so_info_parent",
          info );
    }

    //////////////////////////
    // Info::SurroundingSphere
    QtProperty *surroundingsphere;
    surroundingsphere = createGroup( tr("Surrounding Sphere"), "sceneobject_surroundingsphere", info );

    // Clean
    createVariantLabel( QVariant::Double, "Clean", obj->getSurroundingSphereClean().getRadius(), "so_surroundingsphere_clean", surroundingsphere );

    // Total
    createVariantLabel( QVariant::Double, "Total", obj->getSurroundingSphere().getRadius(), "so_surroundingsphere_total", surroundingsphere );

    /////////////////
    // Representation
    QtProperty *representation;
    representation = createGroup( "Representation", "sceneobject_representation", sceneobject );

    // Visible
    createVariantProperty( QVariant::Int, "Visible", obj->isVisible(), "so_visible", representation );

    // Collapsed
    createVariantProperty( QVariant::Int, "Collapsed", obj->isCollapsed(), "so_collapsed", representation );

    /////////
    // Visual
    QtProperty *visual;
    visual = createGroup( "Visual", "sceneobject_visual", sceneobject );

    // Lighted
    createVariantProperty( QVariant::Int, "Lighted", obj->isLighted(), "so_lighted", visual );

    // Opaque
    createVariantProperty( QVariant::Int, "Opaque", obj->isOpaque(), "so_opaque", visual );

    // Color (if not lighted)
    QtProperty *color;
    GMlib::Color c = obj->getColor();
    color = createVariantProperty( QVariant::Color, tr("Color"), QColor( c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha() ), "so_color", visual );

    // Material (if lighted)
    GMlib::Material m = obj->getMaterial();
    QtProperty *material, *material_ambient, *material_diffuse, *material_specular;
    material = createGroup( "Material", "sceneobject_visual_material", visual, true );
    GMlib::Color m_amb = m.getAmb();
    GMlib::Color m_dif = m.getDif();
    GMlib::Color m_spc = m.getSpc();
    float m_shininess = m.getShininess();
    material_ambient = createVariantProperty( QVariant::Color, "Ambient", QColor( m_amb.getRed(), m_amb.getGreen(), m_amb.getBlue(), m_amb.getAlpha() ), "so_material_ambient", material );
    material_diffuse = createVariantProperty( QVariant::Color, "Diffuse", QColor( m_dif.getRed(), m_dif.getGreen(), m_dif.getBlue(), m_dif.getAlpha() ), "so_material_diffuse", material );
    material_specular = createVariantProperty( QVariant::Color, "Specular", QColor( m_spc.getRed(), m_spc.getGreen(), m_spc.getBlue(), m_spc.getAlpha() ), "so_material_specular", material );
    createVariantProperty( QVariant::Double, "Shininess", QString("%1").arg(m_shininess), "so_material_shininess", material );
  }

  void SceneObjectPropertyBrowser::_setupPSphereFloatProperties( GMlib::PSphere<float>* obj ) {

    ////////////////
    // PSphere Group
    QtProperty *psphere;
    psphere = createGroup( "PSphere<float>", "psphere", getBrowser(), true );
    setBackgroundColor( psphere, QColor( "Pink" ) );

    // Radius
    createVariantProperty( QVariant::Double, "Radius", obj->getRadius(), "psphere_float_radius", psphere );
  }

  void SceneObjectPropertyBrowser::_setupPTorusFloatProperties( GMlib::PTorus<float>* obj ) {

    //////////////
    // PTorus Group
    QtProperty *ptorus;
    ptorus = createGroup( "PTorus<float>", "ptorus", getBrowser(), true );
    setBackgroundColor( ptorus, QColor( "Pink" ) );

    // Wheelradius
    createVariantProperty( QVariant::Double, "Wheel Radius", obj->getWheelRadius(), "ptorus_float_wheel_radius", ptorus );
    createVariantProperty( QVariant::Double, "Tube Radius #1", obj->getTubeRadius1(), "ptorus_float_tube_radius_1", ptorus );
    createVariantProperty( QVariant::Double, "Tube Radius #2", obj->getTubeRadius2(), "ptorus_float_tube_radius_2", ptorus );
  }

  void SceneObjectPropertyBrowser::_slotSurfaceForcedReplot() {

    GMlib::PSurf<float> *psurf_float = dynamic_cast< GMlib::PSurf<float>* >( _sceneobject );
    if( psurf_float )
      psurf_float->replot();
  }

  void SceneObjectPropertyBrowser::_slotSurfacePlotPropertyChanged( QtProperty* property, const GMlib::Point<int,2>& no_samp, const GMlib::Point<int,2>& no_der ) {

    GMlib::PSurf<float> *psurf_float = dynamic_cast< GMlib::PSurf<float>* >( _sceneobject );
    if( !psurf_float )
      return;

    QVariant id = getPropertyId( property );

    if( id == "psurf_plot" )
      psurf_float->replot( no_samp(0), no_samp(1), no_der(0), no_der(1) );
  }

  void SceneObjectPropertyBrowser::_updatePSphereFloatProperties( GMlib::PSphere<float>* obj, bool force ) {

    QVariant value;
    PROPERTYSTATUS ps;

    value = getVariantPropertyValue( "psphere_float_radius", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setRadius( value.toFloat() );
  }

  void SceneObjectPropertyBrowser::_updatePTorusFloatProperties( GMlib::PTorus<float>* obj, bool force ) {

    QVariant value;
    PROPERTYSTATUS ps;

    // Wheel Radius
    value = getVariantPropertyValue( "ptorus_float_wheel_radius", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setWheelRadius( value.toFloat() );

    // Tube Radius 1
    value = getVariantPropertyValue( "ptorus_float_tube_radius_1", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setTubeRadius1( value.toFloat() );

    // Tube Radius 2
    value = getVariantPropertyValue( "ptorus_float_tube_radius_2", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setTubeRadius2( value.toFloat() );
  }

  void SceneObjectPropertyBrowser::_updateSceneObjectProperties( GMlib::SceneObject* obj, bool force ) {

    QVariant value;
    PROPERTYSTATUS ps;

    // Visible
    value = getVariantPropertyValue( "so_visible", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setVisible( value.toBool() );

    // Collapsed
    value = getVariantPropertyValue( "so_collapsed", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setCollapsed( value.toBool() );

    // Lighted
    value = getVariantPropertyValue( "so_lighted", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setLighted( value.toBool() );

    // Opaque
    value = getVariantPropertyValue( "so_opaque", ps );
    if( ps == PROPERTY_CHANGED || force )
      obj->setOpaque( value.toBool() );

    // Color
    value = getVariantPropertyValue( "so_color", ps );
    if( ps == PROPERTY_CHANGED || force ) {

      QColor c = value.value<QColor>();
      obj->setColor( GMlib::Color( c.red(), c.green(), c.blue(), c.alpha() ) );
    }

    // Material
    QVariant m_amb, m_dif, m_spc, m_shi;
    PROPERTYSTATUS ps_amb, ps_dif, ps_spc, ps_shi;
    m_amb = getVariantPropertyValue( "so_material_ambient", ps_amb );
    m_dif = getVariantPropertyValue( "so_material_diffuse", ps_dif );
    m_spc = getVariantPropertyValue( "so_material_specular", ps_spc );
    m_shi = getVariantPropertyValue( "so_material_shininess", ps_shi );
    if( ps_amb == PROPERTY_CHANGED || ps_dif == PROPERTY_CHANGED ||
        ps_spc == PROPERTY_CHANGED || ps_shi == PROPERTY_CHANGED || force ) {

      QColor c;
      GMlib::Material mat = obj->getMaterial();
      if( ps_amb == PROPERTY_CHANGED || force ) {

        c = m_amb.value<QColor>();
        mat.setAmb( GMlib::Color( c.red(), c.green(), c.blue(), c.alpha() ) );
      }
      if( m_dif == PROPERTY_CHANGED || force ) {

        c = m_amb.value<QColor>();
        mat.setDif( GMlib::Color( c.red(), c.green(), c.blue(), c.alpha() ) );
      }
      if( m_spc == PROPERTY_CHANGED || force ) {

        c = m_amb.value<QColor>();
        mat.setSpc( GMlib::Color( c.red(), c.green(), c.blue(), c.alpha() ) );
      }
      if( m_shi == PROPERTY_CHANGED || force )
        mat.set( m_shi.toFloat() );

      obj->setMaterial( mat );
    }
  }

  void SceneObjectPropertyBrowser::createNewObject( GMlib::SceneObject** obj_ptr ) {

    GMlib::SceneObject *obj = 0x0;
    QString type = getCreateType();

    if( type == "pcircle_float" ) {

      qDebug() << "Creating Object: PCircle<float>";
      GMlib::PCircle<float> *circle = new GMlib::PCircle<float>();

      // Property handle
      QtVariantProperty* property;

      // Radius
      property = getVariantProperty( "pcircle_float_radius" );
      if( property )
        circle->setRadius( property->value().toFloat() );

      obj = circle;
    }
    else if( type == "ptorus_float" ) {

      qDebug() << "Creating Object: PTorus<float>";
      GMlib::PTorus<float> *torus = new GMlib::PTorus<float>();

      // Property handle
      QtVariantProperty* property;

      // Tube Radius 1
      property = getVariantProperty( "ptorus_float_tube_radius_1" );
      if(property)
        torus->setTubeRadius1( property->value().toFloat() );

      // Tube Radius 2
      property = getVariantProperty( "ptorus_float_tube_radius_2" );
      if(property)
        torus->setTubeRadius2( property->value().toFloat() );

      // Wheel Radius
      property = getVariantProperty( "ptorus_float_wheel_radius" );
      if(property)
        torus->setWheelRadius( property->value().toFloat() );

      obj = torus;
    }
    else if( type == "psphere_float" ) {

      qDebug() << "Creating Object: PSphere<float>";
      GMlib::PSphere<float> *sphere = new GMlib::PSphere<float>();

      // Property handle
      QtVariantProperty* property;

      // Radius
      property = getVariantProperty( "psphere_float_radius" );
      if( property )
        sphere->setRadius( property->value().toFloat() );

      obj = sphere;

    }

    *obj_ptr = obj;
  }

  void SceneObjectPropertyBrowser::beginSetup() {

    PropertyBrowser::beginSetup();


    disconnect( this, SIGNAL(signPropertyChanged()), this, SLOT(update()) );
    _plot_manager->disconnect();
    _sceneobject = 0x0;

    setCreateType( "" );
  }

  const QString& SceneObjectPropertyBrowser::getCreateType() const {

    return _create_type;
  }

  void SceneObjectPropertyBrowser::endSetup() {

    PropertyBrowser::endSetup();

    connect( this, SIGNAL(signPropertyChanged()), this, SLOT(update()) );

    connect( _plot_manager, SIGNAL(valueChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>)), this, SLOT(_slotSurfacePlotPropertyChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>)) );
    connect( _plot_manager, SIGNAL(signForcedReplot()), this, SLOT(_slotSurfaceForcedReplot()) );
  }

  void SceneObjectPropertyBrowser::set( GMlib::SceneObject* obj, bool store_obj ) {

    beginSetup();

    if( !obj )
      return;

    if( store_obj )
      _sceneobject = obj;

    _setupSceneObjectProperties( obj );

    GMlib::DisplayObject *obj_disp = dynamic_cast< GMlib::DisplayObject*>( obj );
    if( obj_disp ) {

//      _setupDisplayObjectProperties( obj_disp );

      GMlib::PCurve<float> *obj_pcurve_float = dynamic_cast< GMlib::PCurve<float>* >( obj );
      GMlib::PSurf<float> *obj_psurf_float = dynamic_cast< GMlib::PSurf<float>* >( obj );
      if( obj_pcurve_float ) {

        _setupPCurveFloatProperties( obj_pcurve_float );

        GMlib::PCircle<float> *obj_pcircle_float = dynamic_cast< GMlib::PCircle<float>* >( obj );
        if( obj_pcircle_float ) {

          _setupPCircleFloatProperties( obj_pcircle_float );
          setCreateType( "pcircle_float" );
        }
      }
      else if( obj_psurf_float ) {

        _setupPSurfFloatProperties( obj_psurf_float );

        GMlib::PSphere<float> *obj_psphere_float = dynamic_cast< GMlib::PSphere<float>* >( obj );
        GMlib::PTorus<float> *obj_ptorus_float = dynamic_cast< GMlib::PTorus<float>* >( obj );
        if( obj_ptorus_float ) {

          _setupPTorusFloatProperties( obj_ptorus_float );
          setCreateType( "ptorus_float" );
        }
        else if( obj_psphere_float) {

          _setupPSphereFloatProperties( obj_psphere_float );
          setCreateType( "psphere_float" );
        }
//        GMlib::PERBSSurf<float> *obj_perbssurf_float = dynamic_cast< GMlib::PERBSSurf<float>* >( obj );
  //      if( _perbssurf_float ) {
  //
  //        _setupPERBSSurfFloatProperties( obj_perbssurf_float );
  //      }
      }
    }

    endSetup();
  }

  void SceneObjectPropertyBrowser::setCreateType( const QString& type ) {

    _create_type = type;
  }

  void SceneObjectPropertyBrowser::update() {

    update( dynamic_cast<GMlib::SceneObject*>( _sceneobject), false );
  }


  void SceneObjectPropertyBrowser::update( GMlib::SceneObject *obj, bool force ) {

    if( !obj )
      return;

    _updateSceneObjectProperties( obj, force );

    GMlib::DisplayObject *obj_disp = dynamic_cast< GMlib::DisplayObject*>( obj );
    if( obj_disp ) {

      GMlib::PCurve<float> *obj_pcurve_float = dynamic_cast< GMlib::PCurve<float>* >( obj );
      GMlib::PSurf<float> *obj_psurf_float = dynamic_cast< GMlib::PSurf<float>* >( obj );
      if( obj_pcurve_float ) {


      }
      else if( obj_psurf_float ) {

        GMlib::PSphere<float> *obj_psphere_float = dynamic_cast< GMlib::PSphere<float>* >( obj );
        GMlib::PTorus<float> *obj_ptorus_float = dynamic_cast< GMlib::PTorus<float>* >( obj );
        if( obj_psphere_float ) {

          _updatePSphereFloatProperties( obj_psphere_float, force );
        }
        else if( obj_ptorus_float ) {

          _updatePTorusFloatProperties( obj_ptorus_float, force );
        }
      }
    }


    endUpdate();
  }




} // END namespace GMQtExt
